{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Cvxpylayers tutorial"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import cvxpy as cp\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import torch\n",
    "from cvxpylayers.torch import CvxpyLayer\n",
    "torch.set_default_dtype(torch.double)\n",
    "\n",
    "np.set_printoptions(precision=3, suppress=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Parametrized convex optimization problem\n",
    "\n",
    "$$\n",
    "\\begin{array}{ll} \\mbox{minimize} & f_0(x;\\theta)\\\\\n",
    "\\mbox{subject to} & f_i(x;\\theta) \\leq 0, \\quad i=1, \\ldots, m\\\\\n",
    "& A(\\theta)x=b(\\theta),\n",
    "\\end{array}\n",
    "$$\n",
    "with variable $x \\in \\mathbf{R}^n$ and parameters $\\theta\\in\\Theta\\subseteq\\mathbf{R}^p$\n",
    "\n",
    "* objective and inequality constraints $f_0, \\ldots, f_m$ are *convex* in $x$ for each $\\theta$, *i.e.*, their graphs curve upward\n",
    "* equality constraints are linear\n",
    "* for a given value of $\\theta$, find a value for $x$ that minimizes objective, while satisfying constraints\n",
    "* we can efficiently solve these globally with near total reliability"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Solution map\n",
    "* Solution $x^\\star$ is an implicit function of $\\theta$\n",
    "* When unique, define solution map as function\n",
    "$x^\\star = \\mathcal S(\\theta)$\n",
    "* Need to call numerical solver to evaluate\n",
    "* This function is often differentiable\n",
    "* In a series of papers we showed how to analytically differentiate this function, using the implicit function theorem\n",
    "* Benefits of analytical differentiation: works with nonsmooth objective/constraints, low memory usage, don't compound errors"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## CVXPY\n",
    "* High level domain-specific language (DSL) for convex optimization\n",
    "* Define variables, parameters, objective and constraints\n",
    "* Synthesize into problem object, then call solve method\n",
    "* We've added derivatives to CVXPY (forward and backward)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## CVXPYlayers\n",
    "![](https://github.com/cvxgrp/cvxpylayers/blob/master/cvxpylayers_logo.png?raw=true)\n",
    "* Convert CVXPY problems into callable, differentiable Pytorch and Tensorflow modules in one line"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Applications\n",
    "* learning convex optimization models (structured prediction): https://stanford.edu/~boyd/papers/learning_copt_models.html\n",
    "* learning decision-making policies (reinforcement learning): https://stanford.edu/~boyd/papers/learning_cocps.html\n",
    "* machine learning hyper-parameter tuning and feature engineering: https://stanford.edu/~boyd/papers/lsat.html\n",
    "* repairing infeasible or unbounded optimization problems: https://stanford.edu/~boyd/papers/auto_repair_cvx.html\n",
    "* as protection layers in neural networks: http://physbam.stanford.edu/~fedkiw/papers/stanford2019-10.pdf\n",
    "* custom neural network layers (sparsemax, csoftmax, csparsemax, LML): https://locuslab.github.io/2019-10-28-cvxpylayers/\n",
    "* and many more... "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Average example\n",
    "Find the average of a vector:\n",
    "\\begin{equation}\n",
    "\\begin{array}{ll}\n",
    "\\mbox{minimize} & \\sum_{i=1}^n (y_i - x)^2\n",
    "\\end{array}\n",
    "\\end{equation}\n",
    "Variable $x$, parameters $y\\in\\mathbf{R}^n$\n",
    "\n",
    "The solution map is clearly:\n",
    "$$x=\\sum_{i=1}^n y_i / n$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "solution: -0.380\n",
      "analytical solution: -0.380\n"
     ]
    }
   ],
   "source": [
    "n = 7\n",
    "\n",
    "# Define variables & parameters\n",
    "x = cp.Variable()\n",
    "y = cp.Parameter(n)\n",
    "\n",
    "# Define objective and constraints\n",
    "objective = cp.sum_squares(y - x)\n",
    "constraints = []\n",
    "\n",
    "# Synthesize problem\n",
    "prob = cp.Problem(cp.Minimize(objective), constraints)\n",
    "\n",
    "# Set parameter values\n",
    "y.value = np.random.randn(n)\n",
    "\n",
    "# Solve problem in one line\n",
    "prob.solve(requires_grad=True)\n",
    "print(\"solution:\", \"%.3f\" % x.value)\n",
    "print(\"analytical solution:\", \"%.3f\" % np.mean(y.value))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The gradient is simply:\n",
    "$$\\nabla_y x = (1/n)\\mathbf{1}$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gradient: [0.143 0.143 0.143 0.143 0.143 0.143 0.143]\n",
      "analytical gradient: [0.143 0.143 0.143 0.143 0.143 0.143 0.143]\n"
     ]
    }
   ],
   "source": [
    "# Set gradient wrt x\n",
    "x.gradient = np.array([1.])\n",
    "\n",
    "# Differentiate in one line\n",
    "prob.backward()\n",
    "print(\"gradient:\", y.gradient)\n",
    "print(\"analytical gradient:\", np.ones(y.size) / n)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Median example\n",
    "Finding the median of a vector:\n",
    "\\begin{equation}\n",
    "\\begin{array}{ll}\n",
    "\\mbox{minimize} & \\sum_{i=1}^n |y_i - x|,\n",
    "\\end{array}\n",
    "\\end{equation}\n",
    "Variable $x$, parameters $y\\in\\mathbf{R}^n$\n",
    "\n",
    "Solution:\n",
    "$$x=\\mathbf{median}(y)$$\n",
    "\n",
    "Gradient (no duplicates):\n",
    "$$(\\nabla_y x)_i = \\begin{cases}\n",
    "1 & y_i = \\mathbf{median}(y) \\\\\n",
    "0 & \\text{otherwise}.\n",
    "\\end{cases}$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "solution: -0.350\n",
      "analytical solution: -0.350\n"
     ]
    }
   ],
   "source": [
    "n = 7\n",
    "\n",
    "# Define variables & parameters\n",
    "x = cp.Variable()\n",
    "y = cp.Parameter(n)\n",
    "\n",
    "# Define objective and constraints\n",
    "objective = cp.norm1(y - x)\n",
    "constraints = []\n",
    "\n",
    "# Synthesize problem\n",
    "prob = cp.Problem(cp.Minimize(objective), constraints)\n",
    "\n",
    "# Set parameter values\n",
    "y.value = np.random.randn(n)\n",
    "\n",
    "# Solve problem in one line\n",
    "prob.solve(requires_grad=True)\n",
    "print(\"solution:\", \"%.3f\" % x.value)\n",
    "print(\"analytical solution:\", \"%.3f\" % np.median(y.value))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gradient: [-0.  0. -0.  1.  0. -0.  0.]\n",
      "analytical gradient: [0. 0. 0. 1. 0. 0. 0.]\n"
     ]
    }
   ],
   "source": [
    "# Set gradient wrt x\n",
    "x.gradient = np.array([1.])\n",
    "\n",
    "# Differentiate in one line\n",
    "prob.backward()\n",
    "print(\"gradient:\", y.gradient)\n",
    "g = np.zeros(y.size)\n",
    "g[y.value == np.median(y.value)] = 1.\n",
    "print(\"analytical gradient:\", g)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Elastic-net regression example \n",
    "We are given training data $(x_i, y_i)_{i=1}^{N}$,\n",
    "where $x_i\\in\\mathbf{R}$ are inputs and $y_i\\in\\mathbf{R}$ are outputs.\n",
    "Suppose we fit a model for this regression problem by solving the elastic-net problem\n",
    "\\begin{equation}\n",
    "\\begin{array}{ll}\n",
    "\\mbox{minimize} & \\frac{1}{N}\\sum_{i=1}^N (ax_i + b - y_i)^2 + \\lambda |a| + \\alpha a^2,\n",
    "\\end{array}\n",
    "\\label{eq:trainlinear}\n",
    "\\end{equation}\n",
    "where $\\lambda,\\alpha>0$ are hyper-parameters.\n",
    "\n",
    "We hope that the test loss $\\mathcal{L}^{\\mathrm{test}}(a,b) =\n",
    "\\frac{1}{M}\\sum_{i=1}^M (a\\tilde x_i + b - \\tilde y_i)^2$ is small, where\n",
    "$(\\tilde x_i, \\tilde y_i)_{i=1}^{M}$ is our test set.\n",
    "\n",
    "First, we set up our problem, where $\\{x_i, y_i\\}_{i=1}^N$, $\\lambda$, and $\\alpha$ are our parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.datasets import make_blobs\n",
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "torch.manual_seed(0)\n",
    "np.random.seed(0)\n",
    "n = 2\n",
    "N = 60\n",
    "X, y = make_blobs(N, n, centers=np.array([[2, 2], [-2, -2]]), cluster_std=3)\n",
    "Xtrain, Xtest, ytrain, ytest = train_test_split(X, y, test_size=.5)\n",
    "\n",
    "Xtrain, Xtest, ytrain, ytest = map(\n",
    "    torch.from_numpy, [Xtrain, Xtest, ytrain, ytest])\n",
    "Xtrain.requires_grad_(True)\n",
    "m = Xtrain.shape[0]\n",
    "\n",
    "a = cp.Variable((n, 1))\n",
    "b = cp.Variable((1, 1))\n",
    "X = cp.Parameter((m, n))\n",
    "Y = ytrain.numpy()[:, np.newaxis]\n",
    "\n",
    "log_likelihood = (1. / m) * cp.sum(\n",
    "    cp.multiply(Y, X @ a + b) - cp.logistic(X @ a + b)\n",
    ")\n",
    "regularization = - 0.1 * cp.norm(a, 1) - 0.1 * cp.sum_squares(a)\n",
    "prob = cp.Problem(cp.Maximize(log_likelihood + regularization))\n",
    "fit_logreg = CvxpyLayer(prob, [X], [a, b])\n",
    "\n",
    "torch.manual_seed(0)\n",
    "np.random.seed(0)\n",
    "n = 1\n",
    "N = 60\n",
    "X = np.random.randn(N, n)\n",
    "theta = np.random.randn(n)\n",
    "y = X @ theta + .5 * np.random.randn(N)\n",
    "Xtrain, Xtest, ytrain, ytest = train_test_split(X, y, test_size=.5)\n",
    "Xtrain, Xtest, ytrain, ytest = map(\n",
    "    torch.from_numpy, [Xtrain, Xtest, ytrain, ytest])\n",
    "Xtrain.requires_grad_(True)\n",
    "\n",
    "m = Xtrain.shape[0]\n",
    "\n",
    "# set up variables and parameters\n",
    "a = cp.Variable(n)\n",
    "b = cp.Variable()\n",
    "X = cp.Parameter((m, n))\n",
    "Y = cp.Parameter(m)\n",
    "lam = cp.Parameter(nonneg=True)\n",
    "alpha = cp.Parameter(nonneg=True)\n",
    "\n",
    "# set up objective\n",
    "loss = (1/m)*cp.sum(cp.square(X @ a + b - Y))\n",
    "reg = lam * cp.norm1(a) + alpha * cp.sum_squares(a)\n",
    "objective = loss + reg\n",
    "\n",
    "# set up constraints\n",
    "constraints = []\n",
    "\n",
    "prob = cp.Problem(cp.Minimize(objective), constraints)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([[-0.6603]], grad_fn=<_CvxpyLayerFnFnBackward>),\n",
       " tensor([0.1356], grad_fn=<_CvxpyLayerFnFnBackward>))"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# convert into pytorch layer in one line\n",
    "fit_lr = CvxpyLayer(prob, [X, Y, lam, alpha], [a, b])\n",
    "# this object is now callable with pytorch tensors\n",
    "fit_lr(Xtrain, ytrain, torch.zeros(1), torch.zeros(1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# sweep over values of alpha, holding lambda=0, evaluating the gradient along the way\n",
    "alphas = np.logspace(-3, 2, 200)\n",
    "test_losses = []\n",
    "grads = []\n",
    "for alpha_vals in alphas:\n",
    "    alpha_tch = torch.tensor([alpha_vals], requires_grad=True)\n",
    "    alpha_tch.grad = None\n",
    "    a_tch, b_tch = fit_lr(Xtrain, ytrain, torch.zeros(1), alpha_tch)\n",
    "    test_loss = (Xtest @ a_tch.flatten() + b_tch - ytest).pow(2).mean()\n",
    "    test_loss.backward()\n",
    "    test_losses.append(test_loss.item())\n",
    "    grads.append(alpha_tch.grad.item())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.semilogx()\n",
    "plt.plot(alphas, test_losses, label='test loss')\n",
    "plt.plot(alphas, grads, label='analytical gradient')\n",
    "plt.plot(alphas[:-1], np.diff(test_losses) / np.diff(alphas), label='numerical gradient', linestyle='--')\n",
    "plt.legend()\n",
    "plt.xlabel(\"$\\\\alpha$\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# sweep over values of lambda, holding alpha=0, evaluating the gradient along the way\n",
    "lams = np.logspace(-3, 2, 200)\n",
    "test_losses = []\n",
    "grads = []\n",
    "for lam_vals in lams:\n",
    "    lam_tch = torch.tensor([lam_vals], requires_grad=True)\n",
    "    lam_tch.grad = None\n",
    "    a_tch, b_tch = fit_lr(Xtrain, ytrain, lam_tch, torch.zeros(1))\n",
    "    test_loss = (Xtest @ a_tch.flatten() + b_tch - ytest).pow(2).mean()\n",
    "    test_loss.backward()\n",
    "    test_losses.append(test_loss.item())\n",
    "    grads.append(lam_tch.grad.item())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEPCAYAAABShj9RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAgAElEQVR4nO3dd3hUVfrA8e+ZkoRAGglNWoKAEiD0JkoRpLiKKIvK4upiYVcsu/qz7+K6llXRdV3FshZ0bbCKq+ICtpWiokhTem9pkJCeTMrM3PP7Y8IwSSbJBCaZmeT9PE8e5vb3MDNvTs499xyltUYIIUToMwU6ACGEEP4hCV0IIZoJSehCCNFMSEIXQohmQhK6EEI0E5LQhRCimag3oSulFimlspRS22vZrpRSzyml9iultiqlBvs/TCGEEPXxpYb+JjClju1TgV6VP3OBl848LCGEEA1Vb0LXWq8FcuvY5TLgLe3yAxCrlOrkrwCFEEL4xuKHc3QGUj2W0yrXZdZ1UEJCgk5MTPTD5YUQouXYtGnTCa11O2/b/JHQlZd1XscTUErNxdUsQ7du3di4caMfLi+EEC2HUupIbdv80cslDejqsdwFyPC2o9b6Fa31UK310HbtvP6CEUIIcZr8kdCXAddW9nYZCRRoretsbhFCCOF/9Ta5KKUWA+OABKVUGvBnwAqgtX4ZWAFcDOwHbMCcxgpWCCFE7epN6FrrWfVs18At/gjGbreTlpZGWVmZP04nQlRERARdunTBarUGOhQhQoo/bor6TVpaGlFRUSQmJqKUt3utornTWpOTk0NaWhpJSUmBDkeIkBJUj/6XlZURHx8vybwFU0oRHx8vf6UJcRqCqoYOSDIX8hlogKIyO3uPF/n9vI0xkVljzI3WGHE6nAa2CidKgdVswmJWhJlNWMwmLCZFmd1Jqd1JdISVCKsZswlMSmE2uT63JeVOissdlJQ70GgUCqVcn2sFKAU927ehU0wrv8cedAk9kPLz83nvvfeYN2/eaR3/7LPPMnfuXCIjI2tsGzduHE8//TRDhw490zCFcLv/P9v471bpVBZqHp3ej2tGdvf7eSWhe8jPz+fFF188o4R+zTXXeE3oQjSG3ceKGJ7Yllsv7On3czfGH0rK63OIZ3hOP5/SpBStwsyAq7Zud2rsTgOH4XodYTXTymqmqMxOucPAaWichsbQGq2hdbiFqAgLkWFmlFJordG47g9p7fpLpXvbxskRktA93HfffRw4cICBAwdy0UUX8dRTT/HUU0/x/vvvU15ezuWXX85f/vIXSkpKuPLKK0lLS8PpdDJ//nyOHz9ORkYG48ePJyEhgVWrVtV6ncWLF/PXv/4VrTW/+MUvePLJJ3E6ndxwww1s3LgRpRTXX389d9xxB8899xwvv/wyFouF5ORklixZ0oT/IyKYOQ3N0RwbE85tz5je8qCeCOKE/pdPd7Azo9Cv50w+K5o/X9q31u1PPPEE27dv56effgLgiy++YN++ffz4449orZk2bRpr164lOzubs846i+XLlwNQUFBATEwMzzzzDKtWrSIhIaHWa2RkZHDvvfeyadMm4uLimDRpEh9//DFdu3YlPT2d7dtdoxTn5+e7Yzp06BDh4eHudUIAHCsso8Jp0D2+daBDEUEiqHq5BJsvvviCL774gkGDBjF48GB2797Nvn376N+/P1999RX33nsv33zzDTExMT6fc8OGDYwbN4527dphsViYPXs2a9eupUePHhw8eJDbbruNzz77jOjoaABSUlKYPXs277zzDhZL0P7+FQFw5EQJAInx0sQnXII2Q9RVk24qWmvuv/9+fvvb39bYtmnTJlasWMH999/PpEmTePDBB30+pzdxcXH8/PPPfP7557zwwgu8//77LFq0iOXLl7N27VqWLVvGI488wo4dOySxCwAO59gA6J4gNXThIjV0D1FRURQVneoCNnnyZBYtWkRxcTEA6enpZGVlkZGRQWRkJNdccw133XUXmzdv9nq8NyNGjGDNmjWcOHECp9PJ4sWLGTt2LCdOnMAwDGbMmMEjjzzC5s2bMQyD1NRUxo8fz4IFC8jPz3fHIsSRnBLCLCY6RUcEOhQRJKSq5yE+Pp7Ro0fTr18/pk6dylNPPcWuXbsYNWoUAG3atOGdd95h//793H333ZhMJqxWKy+95Jqkae7cuUydOpVOnTrVelO0U6dOPP7444wfPx6tNRdffDGXXXYZP//8M3PmzMEwDAAef/xxnE4n11xzDQUFBWitueOOO4iNjW2a/wwR9A7nlNC9bSQmk/TbFy6qtiaAxjZ06FBdfTz0Xbt20adPn4DEI4KLfBbqN+XZtXSJi+S16+TZhpZEKbVJa+31TZcmFyFCkGFoDueUyA1R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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.semilogx()\n",
    "plt.plot(lams, test_losses, label='test loss')\n",
    "plt.plot(lams, grads, label='analytical gradient')\n",
    "plt.plot(lams[:-1], np.diff(test_losses) / np.diff(lams), label='numerical gradient', linestyle='--')\n",
    "plt.legend()\n",
    "plt.xlabel(\"$\\\\lambda$\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/shane/miniconda3/lib/python3.7/site-packages/matplotlib/patches.py:1306: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray\n",
      "  verts = np.dot(coords, M) + (x + dx, y + dy)\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# compute the gradient of the test loss wrt all the training data points, and plot\n",
    "plt.figure(figsize=(10, 6))\n",
    "a_tch, b_tch = fit_lr(Xtrain, ytrain, torch.tensor([.05]), torch.tensor([.05]), solver_args={\"eps\": 1e-8})\n",
    "test_loss = (Xtest @ a_tch.flatten() + b_tch - ytest).pow(2).mean()\n",
    "test_loss.backward()\n",
    "a_tch_test, b_tch_test = fit_lr(Xtest, ytest, torch.tensor([0.]), torch.tensor([0.]), solver_args={\"eps\": 1e-8})\n",
    "plt.scatter(Xtrain.detach().numpy(), ytrain.numpy(), s=20)\n",
    "plt.plot([-5, 5], [-3*a_tch.item() + b_tch.item(),3*a_tch.item() + b_tch.item()], label='train')\n",
    "plt.plot([-5, 5], [-3*a_tch_test.item() + b_tch_test.item(), 3*a_tch_test.item() + b_tch_test.item()], label='test')\n",
    "Xtrain_np = Xtrain.detach().numpy()\n",
    "Xtrain_grad_np = Xtrain.grad.detach().numpy()\n",
    "ytrain_np = ytrain.numpy()\n",
    "for i in range(Xtrain_np.shape[0]):\n",
    "    plt.arrow(Xtrain_np[i], ytrain_np[i],\n",
    "              -.1 * Xtrain_grad_np[i][0], 0.)\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# move the training data points in the direction of their gradients, and see the train line get closer to the test line\n",
    "plt.figure(figsize=(10, 6))\n",
    "Xtrain_new = torch.from_numpy(Xtrain_np - .15 * Xtrain_grad_np)\n",
    "a_tch, b_tch = fit_lr(Xtrain_new, ytrain, torch.tensor([.05]), torch.tensor([.05]), solver_args={\"eps\": 1e-8})\n",
    "plt.scatter(Xtrain_new.detach().numpy(), ytrain.numpy(), s=20)\n",
    "plt.plot([-5, 5], [-3*a_tch.item() + b_tch.item(),3*a_tch.item() + b_tch.item()], label='train')\n",
    "plt.plot([-5, 5], [-3*a_tch_test.item() + b_tch_test.item(), 3*a_tch_test.item() + b_tch_test.item()], label='test')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "@webio": {
   "lastCommId": null,
   "lastKernelId": null
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
